home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Modules / BackSpaceModules / Source / Sperm / SpermView.m < prev    next >
Text File  |  1993-06-17  |  9KB  |  444 lines

  1. /*
  2.     The animation guts from a freely distributable X program:
  3.     xsperm.c
  4.     Drew Olbrich, Febrary 1991
  5.     Note --  This code originally served as a demonstration
  6.     of how to do animation under X.  The "guts" of the program
  7.     which draws the sperm are consequently located in one huge
  8.     chunk in the update_display() routine, and can be easily
  9.     cut out.
  10.  
  11.     The animation function wrapped in a NeXTstep View subclass by Ali Ozer, May 91
  12.     Very minor changes so this thing works as a screen saver module by sam streeper,
  13.     August 91
  14.     The "oneStep" method computes new locations.
  15. */
  16.  
  17. #import "SpermView.h"
  18. #import "Thinker.h"
  19. #import <appkit/appkit.h>
  20.  
  21. #define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1])
  22. #define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1]))
  23.  
  24. #define VEC_SET(x, a, b) x[0] = a, x[1] = b
  25. #define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1]
  26. #define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1]
  27. #define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1]
  28. #define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1]
  29. #define VEC_MULT(x, a) x[0] *= a, x[1] *= a
  30. #define VEC_DIV(x, a) x[0] /= a, x[1] /= a
  31. #define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1]
  32. #define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); }
  33.  
  34. #define MINRAD 0.1
  35. #define RADSTEP 2.0
  36. #define MAXRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP)
  37. #define INITRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP)
  38.  
  39. // RANDINT(n) returns an integer 0..n-1
  40. // RANDFLOAT(f) returns a float [0..f] (inclusive on both ends)
  41.  
  42. #define RANDINT(n) (random() % (n))
  43. #define RANDFLOAT(f) (((f) * (float)(random() & 0x0ffff)) / (float)0x0ffff)
  44.  
  45. @implementation SpermView
  46.  
  47. - initFrame:(const NXRect *)rect
  48. {
  49.     [super initFrame:rect];
  50.  
  51.     [self allocateGState];        // For faster lock/unlockFocus
  52.  
  53.     dir = 1.0;
  54.     rad = INITRAD;
  55.     [self getSpermCount];
  56.     [self getLineWidth];
  57.     [self getUseColor];
  58.     [inspectorPanel display];
  59.  
  60.     color = NX_COLORWHITE;
  61.     alreadyInitialized = NO;
  62.     randCount1 = 100;
  63.     randCount2 = 200;
  64.     
  65.     uPath = newUserPath();
  66.  
  67.     return self;
  68. }
  69.  
  70. - (void)initializeLine:(int)i
  71. {
  72.     double angle = RANDFLOAT(10.0) + 5.0;
  73.     prevX[i][0] = x[i][0] = (double) (RANDINT((int)NX_WIDTH(&bounds)));
  74.     prevX[i][1] = x[i][1] = (double) (RANDINT((int)NX_HEIGHT(&bounds)));
  75.     v[i][0] = RANDFLOAT(2.0) - 1.0;
  76.     v[i][1] = RANDFLOAT(2.0) - 1.0;    
  77.     sine[i] = sin(angle*M_PI/180.0);
  78.     cosine[i] = cos(angle*M_PI/180.0);
  79.     vel[i] = RANDFLOAT(4.0) + 4.0;
  80.     VEC_NORM(v[i]);
  81. }
  82.  
  83. - (void)getFocusFromEvent:(NXEvent *)event
  84. {
  85.     NXPoint loc = event->location;
  86.     [self convertPoint:&loc fromView:nil];
  87.     mouse[0] = loc.x;
  88.     mouse[1] = loc.y;
  89. }
  90.  
  91. - (BOOL)acceptsFirstMouse
  92. {    return YES;
  93. }
  94.  
  95. - mouseDown:(NXEvent *)event
  96. {
  97.     [self getFocusFromEvent:event];
  98.     return self;
  99. }
  100.  
  101. - effectOne
  102. {
  103.     VECTOR y;
  104.     int i;
  105.  
  106.     dir *= -1.0;
  107.     for (i = 0; i < MAXCOUNT; i++) {
  108.     VEC_COPY(y, v[i]);
  109.     if (dir == -1.0) {
  110.         v[i][0] = y[1];
  111.         v[i][1] = -y[0];
  112.     } else {
  113.         v[i][0] = -y[1];
  114.         v[i][1] = y[0];
  115.     }
  116.     }
  117.     return self;
  118. }
  119.  
  120. - effectTwo
  121. {
  122.     int i;
  123.     for (i = 0; i < MAXCOUNT; i++) {
  124.     v[i][0] = -v[i][0];
  125.     }
  126.     return self;
  127. }
  128.  
  129. - effectThree
  130. {
  131.     int i;
  132.     for (i = 0; i < MAXCOUNT; i++) {
  133.     v[i][1] = -v[i][1];
  134.     }
  135.     return self;
  136. }
  137.  
  138. - effectFour
  139. {
  140.     [self effectTwo];
  141.     [self effectThree];
  142.     return self;
  143. }
  144.  
  145. - effectFive
  146. {
  147.     [self effectOne];
  148.     [self effectFour];
  149.     return self;
  150. }
  151.  
  152. - effectSix
  153. {
  154.     rad = MIN(rad * RADSTEP, MAXRAD);
  155.     return self;
  156. }
  157.  
  158. - effectSeven
  159. {
  160.     rad = MAX(rad / RADSTEP, MINRAD);
  161.     return self;
  162. }
  163.  
  164. - doEffectNumber:(int)val
  165. {
  166.     switch (val) {
  167.     case 0: [self effectOne]; break;
  168.     case 1: [self effectTwo]; break;
  169.     case 2: [self effectThree]; break;
  170.     case 3: [self effectFour]; break;
  171.     case 4: [self effectFive]; break;
  172.     case 5: [self effectSix]; break;
  173.     case 6: [self effectSeven]; break;
  174.     default: break;
  175.     }
  176.     return self;
  177. }
  178.  
  179. - oneStep
  180. {
  181.     int i, cnt;
  182.         POINT lLeft, uRight;
  183.     NXRect eraseRect;
  184.  
  185.     uRight[0] = lLeft[0] = x[0][0];
  186.     uRight[1] = lLeft[1] = x[0][1];
  187.  
  188.     for (i = 0; i < count; i++) {
  189.         VECTOR w, y;
  190.         POINT p;
  191.         double r;
  192.  
  193.         for (cnt = 0; cnt < 2; cnt++) {
  194.             if (prevX[i][cnt] < lLeft[cnt]) lLeft[cnt] = prevX[i][cnt];
  195.             else if (prevX[i][cnt] > uRight[cnt]) uRight[cnt] = prevX[i][cnt];
  196.             if (x[i][cnt] < lLeft[cnt]) lLeft[cnt] = x[i][cnt];
  197.             else if (x[i][cnt] > uRight[cnt]) uRight[cnt] = x[i][cnt];
  198.         }
  199.  
  200.         prevX[i][0] = x[i][0];    /* old location */
  201.         prevX[i][1] = x[i][1];
  202.     
  203.         VEC_SUB(w, x[i], mouse);
  204.         VEC_NORM(w);
  205.         VEC_COPY(y, w);
  206.         w[0] = y[0]*cosine[i] - dir*y[1]*sine[i];
  207.         w[1] = y[1]*cosine[i] + dir*y[0]*sine[i];
  208.         VEC_ADDS(p, mouse, rad*(160.0 - vel[i]*20.0), w);
  209.     
  210.         VEC_SUB(w, p, x[i]);
  211.         r = VEC_LEN(w);
  212.         VEC_DIV(w, r);
  213.     
  214.         VEC_ADDS(v[i], v[i], 1.0, w);
  215.     
  216.         VEC_NORM(v[i]);
  217.         VEC_MULT(v[i], vel[i]);
  218.     
  219.         VEC_ADD(x[i], x[i], v[i]);
  220.  
  221.     }
  222.  
  223.     NXSetRect (&eraseRect, lLeft[0], lLeft[1], uRight[0]-lLeft[0], uRight[1]-lLeft[1]);
  224.     NXInsetRect (&eraseRect, -1.0-lineWidth, -1.0-lineWidth);
  225.     PSsetgray(0);
  226.     NXRectFill(&eraseRect);
  227.  
  228.     [self drawPath];
  229.  
  230.     if (--randCount1 < 0)
  231.     {
  232.         randCount1 = RANDINT(700);
  233.         mouse[0] = randBetween(0,bounds.size.width);
  234.         mouse[1] = randBetween(0,bounds.size.height);
  235.     }
  236.     if (--randCount2 < 0)
  237.     {
  238.         randCount2 = RANDINT(600);
  239.         [self doEffectNumber:(randCount2 % 7)];
  240.     }
  241.     return self;
  242. }
  243.  
  244. // Modify "orig" by upto plus or minus "by" keeping it in the specified range...
  245.  
  246. static float randMod(float orig, float by, float min, float max)
  247. {
  248.     orig = orig + RANDFLOAT(by * 2.0) - by;
  249.     return (orig < min) ? min : ((orig > max) ? max : orig);
  250. }
  251.  
  252. - drawPath
  253. {
  254.     int cnt;
  255.  
  256.     PSsetlinewidth (lineWidth);
  257.     if (useColors) {
  258.     color = NXConvertRGBToColor(randMod(NXRedComponent(color), 0.05, 0.0, 1.0),
  259.         randMod(NXGreenComponent(color), 0.05, 0.0, 1.0), 
  260.         randMod(NXBlueComponent(color), 0.05, 0.0, 1.0));
  261.     }
  262.     else color = NX_COLORWHITE;
  263.     
  264.     NXSetColor (color);
  265.  
  266.     beginUserPath(uPath, NO);
  267.     for (cnt = 0; cnt < count; cnt++) {
  268.     UPmoveto(uPath, (float)prevX[cnt][0], (float)prevX[cnt][1]);
  269.     UPlineto(uPath, (float)x[cnt][0], (float)x[cnt][1]);
  270.     }
  271.     closePath(uPath);
  272.     endUserPath(uPath, dps_ustroke);
  273.     sendUserPath(uPath);
  274.  
  275.     return self;
  276. }
  277.     
  278. - drawSelf:(const NXRect *)rects :(int)rectCount
  279. {
  280.     if (!rects || !rectCount) return self;
  281.     
  282.     PSsetgray(NX_BLACK);
  283.     NXRectFill(rects);
  284.     [self drawPath];
  285.     return self;
  286. }
  287.  
  288. - newWindow
  289. {
  290.     mouse[0] = randBetween(0,bounds.size.width);
  291.     mouse[1] = randBetween(0,bounds.size.height);
  292.  
  293.     return self;
  294. }
  295.  
  296. - free
  297. {
  298.     freeUserPath(uPath);
  299.     return [super free];
  300. }
  301.  
  302. - setNumLines:sender
  303. {
  304.     int       i;
  305.     int    oldCount = count;
  306.     char str[100];
  307.  
  308.     // set the number of lines
  309.     count = MIN(MAXCOUNT, MAX([sender intValue], 1));
  310.  
  311.     // initialize velocities & such
  312.     for (i = oldCount; i < count; i++) {
  313.         [self initializeLine:i];
  314.     }
  315.  
  316.     [self display];
  317.  
  318.     sprintf(str,"%d", count);
  319.     NXWriteDefault([NXApp appName], "SpermViewCount", str);
  320.  
  321.     return self;
  322. }
  323.  
  324. - getSpermCount
  325. {
  326.     const char *ptr;
  327.     int val;
  328.  
  329.     [spermCountSlider setMinValue: 10];
  330.     [spermCountSlider setMaxValue: MAXCOUNT];
  331.     
  332.     ptr = NXGetDefaultValue([NXApp appName], "SpermViewCount");
  333.     if (ptr)
  334.     {
  335.         sscanf(ptr,"%d",&val);
  336.         if (val >= 10 && val <= MAXCOUNT) count = val;
  337.         else count = MAXCOUNT;
  338.     }
  339.     else count = MAXCOUNT;
  340.     
  341.     return self;
  342. }
  343.  
  344. - setUseColor:sender
  345. {
  346.     useColors = [sender state];
  347.  
  348.     if (useColors)
  349.         NXWriteDefault([NXApp appName], "SpermViewColor", "Yes");
  350.     else
  351.         NXRemoveDefault([NXApp appName], "SpermViewColor");
  352.  
  353.     return self;
  354. }
  355.  
  356. - getUseColor
  357. {
  358.     const char *ptr;
  359.     
  360.     ptr = NXGetDefaultValue([NXApp appName], "SpermViewColor");
  361.  
  362.     if (!ptr || !strcmp(ptr,"No")) useColors = NO;
  363.     else useColors = YES;
  364.     
  365.     return self;
  366. }
  367.  
  368. - setLineWidth:sender
  369. {
  370.     char str[50];
  371.  
  372.     lineWidth = MAX([sender floatValue], 0.0);
  373.     sprintf(str,"%5.1f", lineWidth);
  374.     NXWriteDefault([NXApp appName], "SpermViewWidth", str);
  375.  
  376.     return self;
  377. }
  378.  
  379. - getLineWidth
  380. {
  381.     const char *ptr;
  382.     float val;
  383.  
  384.     [spermWidthSlider setMinValue: 0];
  385.     [spermWidthSlider setMaxValue: 8];
  386.     
  387.     ptr = NXGetDefaultValue([NXApp appName], "SpermViewWidth");
  388.     if (ptr)
  389.     {
  390.         sscanf(ptr,"%f",&val);
  391.         if (val >= 0 && val <= 8) lineWidth = val;
  392.         else lineWidth = 0;
  393.     }
  394.     else lineWidth = 0;
  395.     
  396.     return self;
  397. }
  398.  
  399. - sizeTo:(NXCoord)width :(NXCoord)height
  400. {
  401.     [super sizeTo:width :height];
  402.     
  403.     if (!alreadyInitialized)
  404.     {    int i;
  405.         mouse[0] = NX_MIDX(&bounds);
  406.         mouse[1] = NX_MIDY(&bounds);
  407.  
  408.         for (i = 0; i < MAXCOUNT; i++) {
  409.             [self initializeLine:i];
  410.         }
  411.         alreadyInitialized = YES;
  412.     }
  413.  
  414.     [self newWindow];
  415.     return self;
  416. }
  417.  
  418. - (const char *)windowTitle
  419. {    return "Sperm";
  420. }
  421.  
  422. - (BOOL) useBufferedWindow;
  423. {    return YES;
  424. }
  425.  
  426.  
  427. - inspector:sender
  428. {
  429.     char buf[MAXPATHLEN];
  430.     
  431.     if (!inspectorPanel)
  432.     {
  433.         [NXBundle getPath:buf forResource:"sperm" ofType:"nib" inDirectory:[sender moduleDirectory:"Sperm"] withVersion:0];
  434.         [NXApp loadNibFile:buf owner:self withNames:NO];
  435.  
  436.         [spermCountSlider setIntValue:count];
  437.         [spermWidthSlider setFloatValue:lineWidth];
  438.         [colorButton setState: (useColors ? 1:0)];
  439.     }
  440.     return inspectorPanel;
  441. }
  442.  
  443. @end
  444.